Skip to main content
Version: 1.0.0

Ranged Area with Line

Displays a range of values over time, with a line indicating the average or median. This helps in understanding the variability and uncertainty within the data. It is useful for analyzing data with a range of values, such as stock price fluctuations, weather forecasts, or performance metrics with confidence intervals.

Chart:


Code:

  const { muze, getDataFromSearchQuery } = viz;
  const data = getDataFromSearchQuery();
  const { DataModel } = muze;

  const DateTimeFormatter = DataModel.DateTimeFormatter;
  const share = muze.Operators.share;

  const ColumnField = "time";
  const MinDaysField = "minDays";
  const MaxDaysField = "maxDays";
  const DaysAbove32Field = "days at or above 32 deg";
  const SourceYears = ["01-01-1992", "01-01-2018", "01-01-2072"];
  const SubtitleText = "Days at or above 32°C per year from the time you were born";

  muze
    .canvas()
    .rows([
      [],
      [
        share(MinDaysField, DaysAbove32Field, MaxDaysField),
      ],
    ])
    .columns([ColumnField])
    .config({
      legend: {
        color: {
          value: "#bebada",
        },
      },
      gridLines: {
        x: {
          show: true,
        },
      },
      axes: {
        y: {
          domain: [180, 320],
          name: "Average number of days at or above 32˚C",
          numberOfTicks: 8,
        },
      },
    })
    .layers([
      {
        mark: "line",
        className: "line-plot-item",
        encoding: {
          y: DaysAbove32Field,
        },
        interpolate: "catmullRom",
      },
      {
        mark: "area",
        className: "area-layer",
        encoding: {
          y: MinDaysField,
          y0: MaxDaysField,
          color: {
            value: () => "#fb8072",
          },
        },
        transition: {
          duration: 0,
        },
        interpolate: "catmullRom",
      },
      {
        mark: "text",
        className: "text-layer",
        encoding: {
          y: DaysAbove32Field,
          text: {
            field: ColumnField,
            formatter: (dataInfo) =>
              DateTimeFormatter.formatAs(dataInfo.rawValue, "%Y"),
          },
          color: {
            value: () => "#000",
          },
        },
        source: (dt) =>
          dt.select({
            conditions: SourceYears.map((s) => ({
              field: ColumnField,
              value: +new Date(s),
              operator: "eq",
            })),
            operator: "or",
          }),
        encodingTransform: (points) => {
          for (let i = 0, len = points.length; i < len; i++) {
            points[i].update.y -= 10;
            if (i === 0) {
              points[i].update.x -= 30;
            }
            if (i === len - 1) {
              points[i].update.x -= 30;
            }
          }
          return points;
        },
      },
      {
        mark: "point",
        className: "anchor-indicator",
        encoding: {
          y: DaysAbove32Field,
          size: {
            value: 100,
          },
          color: {
            value: () => "#ff0000",
          },
        },
        source: (dt) =>
          dt.select({
            conditions: SourceYears.map((s) => ({
              field: ColumnField,
              value: +new Date(s),
              operator: "eq",
            })),
            operator: "and",
          }),
      },
      {
        mark: "tick",
        className: "tick-layer",
        encoding: {
          y: MaxDaysField,
          y0: MinDaysField,
        },
        source: (dt) =>
          dt.select({
            conditions: SourceYears.map((s) => ({
              field: ColumnField,
              value: +new Date(s),
              operator: "eq",
            })),
            operator: "and",
          }),
      },
    ])
    .subtitle(SubtitleText)
    .data(data)
    .mount("#chart"); // mount your chart